package controladores;

import ciudad.Calle;
import ciudad.Ciudad;
import ciudad.Esquina;
import excepciones.MovimientosMaximosAlcanzadosException;
import excepciones.PuntajeMenorACeroException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import juego.Juego;
import juego.Jugador;
import juego.usuariosGuardados.Usuario;
import juego.usuariosGuardados.UsuariosGuardados;
import obstrucciones.Obstruccion;
import persistores.persistoresDeJugador.JugadorPersistor;
import persistores.persistoresDeJugador.UsuariosGuardadosPersistor;
import vehiculos.Estado;
import vehiculos.Vehiculo;
import vistas.InterfazFinDeJuego;
import vistas.InterfazJuegoPrincipal;
import vistas.VentanaFinDeJuego;


public class ControladorJuegoPrincipal extends Controlador implements ActionListener {
    private InterfazJuegoPrincipal vista;
    private Juego modelo;
    
    public ControladorJuegoPrincipal (InterfazJuegoPrincipal vista, Juego modelo) {
        this.vista = vista;
        this.modelo = modelo;
        Jugador jugador = modelo.getJugador();
        vista.setSaludo(jugador.getNickname());
        vista.setTituloPartida(jugador.getNivel());
        actualizarVista();
        inicializarJuego();
    }
    
    private void inicializarJuego () {
        Jugador jugador = modelo.getJugador();
        int callesVerticales;
        int callesHorizontales;
        callesHorizontales = jugador.getCiudadDeJuego().getCiudad().getCantidadDeCallesHorizontales();
        callesVerticales = 
        jugador.getCiudadDeJuego().getCiudad().getCantidadDeCallesVerticales();
        vista.inicializarCiudad(callesHorizontales, callesVerticales);
        colocarLineaDeLlegada();
        dispersarObstrucciones();
        colocarVehiculoEnPosicion();
    }
    
    private void actualizarVista () {
        if (!modelo.jugadorHaLlegadoAlaMeta()) {
            Jugador jugador = this.modelo.getJugador();
            Vehiculo vehiculo = jugador.getVehiculo();
            vista.setMovimientosMaximos(jugador.getCantidadDeMovimientosMaximos());
            vista.setMovimientosActuales(vehiculo.getCantidadDeMovimientos());
            vista.setPuntaje(jugador.getPuntaje());
            vista.setVehiculo(determinarTipoDeVehiculo());
        }
        else {
            finalizarJuego();
        }
    }
    
    private String determinarTipoDeVehiculo () {
        Vehiculo vehiculo = modelo.getJugador().getVehiculo();
        Estado tipoDeVehiculo = vehiculo.getTipoDeVehiculo();
        String tipo = tipoDeVehiculo.getClass().getSimpleName();
        if (tipo.equals("CuatroPorCuatro")) return "4 x 4";
        else return tipo;
    }
    
    private void colocarLineaDeLlegada () {
        Esquina esquinaDeLlegada = modelo.getJugador().getCiudadDeJuego().getPuntoDeLlegada();
        Ciudad ciudad = modelo.getJugador().getCiudadDeJuego().getCiudad();
        List<Integer> coordenadas = ciudad.getPosicionDeEsquina(esquinaDeLlegada);
        // Calculo las coordenadas de la calle Este correspondiente.
        int x = 2 * coordenadas.get(0) + 1;
        int y = 2 * coordenadas.get(1) + 2;
        vista.colocarLineaDeLlegada (x, y);
    }
    
    @Override
    public void actionPerformed (ActionEvent evento) {
        if (evento.getActionCommand().equals("Volver")) {
            vista.cerrar();
        }
        else if (evento.getActionCommand().equals("Guardar Partida")) {
            guardarPartida();
        }
    
        else if (evento.getActionCommand().equals("Conducir al Norte")) {
            moverVehiculoAlNorte();
        } 
        else if (evento.getActionCommand().equals("Conducir al Sur")){
            moverVehiculoAlSur();
        }
        else if (evento.getActionCommand().equals("Conducir al Este")){
            moverVehiculoAlEste();
        }
        else if (evento.getActionCommand().equals("Conducir al Oeste")){
            moverVehiculoAlOeste();
        }
     
    }
    private void guardarPartida(){
        Jugador jugador = modelo.getJugador();
        guardarUsuario(jugador);
        String nombreJugador = jugador.getNickname();
        String pathArchivo = ".\\" + nombreJugador;
        JugadorPersistor persistor = new JugadorPersistor();
        persistor.serializar(jugador, pathArchivo);
    }
    
        
    private void guardarUsuario(Jugador jugador){
        UsuariosGuardados usuariosGuardados;
        File usuariosGuardadosArchivo = new File(".\\UsuariosGuardados.xml");
        
        String nombreUsuario = jugador.getNickname();
        int puntajeUsuario = jugador.getPuntaje();
        
        Usuario usuario = new Usuario();
        usuario.setNickname(nombreUsuario);
        
        if (usuariosGuardadosArchivo.exists()) {
               
            UsuariosGuardadosPersistor persistor = new UsuariosGuardadosPersistor();
            usuariosGuardados = persistor.deserializar(".\\UsuariosGuardados");
            
            if(usuariosGuardados.usuarioEstaEnUsuariosGuardados(usuario)){
                Usuario usuarioGuardado = usuariosGuardados.getUsuarioConNombre(nombreUsuario);
                
                //Se guarda el puntaje si el puntaje actual obtenido
                //es mayor que el guardado.
                if (usuarioGuardado.getPuntaje() < puntajeUsuario) {
                    usuarioGuardado.setPuntaje(puntajeUsuario);
                }
            } else {
                usuario.setPuntaje(puntajeUsuario);
                usuariosGuardados.agregarUsuario(usuario);
            }
        } else {
            usuariosGuardados = new UsuariosGuardados();
            usuario.setPuntaje(puntajeUsuario);
            usuariosGuardados.agregarUsuario(usuario);
        }
        
        UsuariosGuardadosPersistor persistor = new UsuariosGuardadosPersistor();
        persistor.serializar(usuariosGuardados, ".\\UsuariosGuardados");
    }
    
    private void dispersarObstrucciones() {
        Ciudad ciudad = modelo.getJugador().getCiudadDeJuego().getCiudad();
        List<Obstruccion> obstrucciones = null;
        int cantidadCallesHorizontales = ciudad.getCantidadDeCallesHorizontales();
        int cantidadCallesVerticales = ciudad.getCantidadDeCallesVerticales();
	
        for (int indiceFila = 0; indiceFila < cantidadCallesHorizontales; indiceFila++) {
            for (int indiceColumna = 0; indiceColumna < cantidadCallesVerticales; indiceColumna++) {
                Esquina esquinaActual = ciudad.getEsquinaEnPosicion(indiceFila, indiceColumna);
		
                //Si la esquina se encuentra en la ultima columna de la ciudad,
		//se agregan las obstrucciones en la calle a su derecha (calle Este).
		if (indiceColumna == (cantidadCallesVerticales - 1)) {
                    Calle calleEste = esquinaActual.getCalleEste();
                    if (calleEste.getCantidadDeObstrucciones() != 0) {
                        agregarObstruccionesEnCalle(calleEste, 2*indiceFila+1, 2*indiceColumna+2);
                    }
                }
                //Si la esquina se encuentra en la ultima fila de la ciudad,
	        //se agregan las obstrucciones en la calle Sur.
                if (indiceFila == (cantidadCallesHorizontales - 1)) {
                    Calle calleSur = esquinaActual.getCalleSur();
                    if (calleSur.getCantidadDeObstrucciones() != 0) {
                        agregarObstruccionesEnCalle(calleSur, 2*indiceFila+2, 2*indiceColumna+1);
                    }
                }
                
                //En todos los casos, revisar las obstrucciones en la calleOeste,
		//y las obstrucciones de la calle Norte, asi se evita agregar
                //obstrucciones mas de una vez.
                Calle calleOeste = esquinaActual.getCalleOeste();
                if (calleOeste.getCantidadDeObstrucciones() != 0) {
                    agregarObstruccionesEnCalle(calleOeste, 2*indiceFila+1, 2*indiceColumna);
                }
                
                Calle calleNorte = esquinaActual.getCalleNorte();
		if (calleNorte.getCantidadDeObstrucciones() != 0) {
                    agregarObstruccionesEnCalle(calleNorte, 2*indiceFila, 2*indiceColumna+1);
                }
            }
        }
    }
    
    private void agregarObstruccionesEnCalle (Calle calle, int x, int y) {
        List <Obstruccion> obstrucciones = calle.getObstrucciones();
        List <String> nombresObstrucciones = new ArrayList<>();
        for (int i = 0; i < obstrucciones.size(); i++) {
            Obstruccion obstruccion = obstrucciones.get(i);
            nombresObstrucciones.add(obstruccion.getClass().getSimpleName());
        }
        vista.agregarObstruccionesEnCalle (x, y, nombresObstrucciones);
    }
    
    private void colocarVehiculoEnPosicion() {
        Vehiculo vehiculo = modelo.getJugador().getVehiculo();
        
        Esquina esquina = vehiculo.getEsquinaActual();
        Ciudad ciudad = modelo.getJugador().getCiudadDeJuego().getCiudad();
        List<Integer> coordenadas = ciudad.getPosicionDeEsquina(esquina);
        int x = 2 * coordenadas.get(0) + 1;
        int y = 2 * coordenadas.get(1) + 1;
        vista.colocarVehiculoEnPosicion (x, y, determinarTipoDeVehiculo());
    }
   
     private void moverVehiculoAlNorte() {
        Jugador jugador = modelo.getJugador();
        Vehiculo vehiculo = jugador.getVehiculo();
        Esquina esquina = vehiculo.getEsquinaActual();
        Ciudad ciudad = jugador.getCiudadDeJuego().getCiudad();
        
        List<Integer> coordenadas = ciudad.getPosicionDeEsquina(esquina);
        int posicionInicialX = 2 * coordenadas.get(0) + 1;
        int posicionInicialY = 2 * coordenadas.get(1) + 1;
        int movimientosActuales = vehiculo.getCantidadDeMovimientos();
        
        //Se mueve al vehículo al Norte.
        try {
            jugador.moverVehiculoAlNorte();
        } catch (PuntajeMenorACeroException | MovimientosMaximosAlcanzadosException ex) {
            finalizarJuego();
        }
        
        //Si el vehículo cambió de lugar, se actualiza la vista.
        if (movimientosActuales != vehiculo.getCantidadDeMovimientos()) {
            mostrarSorpresasAtravesadas (esquina.getCalleNorte().getObstrucciones());
            
//Se asigna un fondo transparente a la posición actual. 
            vista.transparentarPanel(posicionInicialX, posicionInicialY);     
            vista.colocarVehiculoEnPosicion (posicionInicialX - 2, posicionInicialY, determinarTipoDeVehiculo());
            actualizarVista();
        }
     }
     
      private void moverVehiculoAlSur() {
        Jugador jugador = modelo.getJugador();
        Vehiculo vehiculo = jugador.getVehiculo();
        Esquina esquina = vehiculo.getEsquinaActual();
        Ciudad ciudad = jugador.getCiudadDeJuego().getCiudad();
        
        List<Integer> coordenadas = ciudad.getPosicionDeEsquina(esquina);
        int posicionInicialX = 2 * coordenadas.get(0) + 1;
        int posicionInicialY = 2 * coordenadas.get(1) + 1;
        int movimientosActuales = vehiculo.getCantidadDeMovimientos();
        
        //Se mueve al vehículo al Sur.
        try {
            jugador.moverVehiculoAlSur();
        } catch (PuntajeMenorACeroException | MovimientosMaximosAlcanzadosException ex) {
           finalizarJuego();
        }
        
        //Si el vehículo cambió de lugar, se actualiza la vista.
        if (movimientosActuales != vehiculo.getCantidadDeMovimientos()) {
            mostrarSorpresasAtravesadas (esquina.getCalleSur().getObstrucciones());
            //Se asigna un fondo transparente a la posición actual. 
            vista.transparentarPanel(posicionInicialX, posicionInicialY);     
            vista.colocarVehiculoEnPosicion (posicionInicialX + 2, posicionInicialY, determinarTipoDeVehiculo());
            actualizarVista();
        }
    }
      
         
    private void moverVehiculoAlEste() {
        Jugador jugador = modelo.getJugador();
        Vehiculo vehiculo = jugador.getVehiculo();
        Esquina esquina = vehiculo.getEsquinaActual();
        Ciudad ciudad = jugador.getCiudadDeJuego().getCiudad();
        
        List<Integer> coordenadas = ciudad.getPosicionDeEsquina(esquina);
        int posicionInicialX = 2 * coordenadas.get(0) + 1;
        int posicionInicialY = 2 * coordenadas.get(1) + 1;
        int movimientosActuales = vehiculo.getCantidadDeMovimientos();
        
        //Se mueve al vehículo al Este.
        try {
            jugador.moverVehiculoAlEste();
        } catch (PuntajeMenorACeroException | MovimientosMaximosAlcanzadosException ex) {
            finalizarJuego();
        }
        
        //Si el vehículo cambió de lugar, se actualiza la vista.
        if (movimientosActuales != vehiculo.getCantidadDeMovimientos()) {
            mostrarSorpresasAtravesadas (esquina.getCalleEste().getObstrucciones());
            
            //Se asigna un fondo transparente a la posición actual. 
            vista.transparentarPanel(posicionInicialX, posicionInicialY);     
            vista.colocarVehiculoEnPosicion (posicionInicialX, posicionInicialY + 2, determinarTipoDeVehiculo());
            actualizarVista();
        }
    }
    
       private void moverVehiculoAlOeste() {
        Jugador jugador = modelo.getJugador();
        Vehiculo vehiculo = jugador.getVehiculo();
        Esquina esquina = vehiculo.getEsquinaActual();
        Ciudad ciudad = jugador.getCiudadDeJuego().getCiudad();
        
        List<Integer> coordenadas = ciudad.getPosicionDeEsquina(esquina);
        int posicionInicialX = 2 * coordenadas.get(0) + 1;
        int posicionInicialY = 2 * coordenadas.get(1) + 1;
        int movimientosActuales = vehiculo.getCantidadDeMovimientos();
        
        //Se mueve al vehículo al Oeste.
        try {
            jugador.moverVehiculoAlOeste();
        } catch (PuntajeMenorACeroException | MovimientosMaximosAlcanzadosException ex) {
           finalizarJuego();
        }
        
        //Si el vehículo cambió de lugar, se actualiza la vista.
        if (movimientosActuales != vehiculo.getCantidadDeMovimientos()) {
            mostrarSorpresasAtravesadas (esquina.getCalleOeste().getObstrucciones());
            
            //Se asigna un fondo transparente a la posición actual. 
            vista.transparentarPanel(posicionInicialX, posicionInicialY);     
            vista.colocarVehiculoEnPosicion (posicionInicialX, posicionInicialY - 2, determinarTipoDeVehiculo());
            actualizarVista();
        }
    }
       
    private void finalizarJuego() {
     Jugador jugador = modelo.getJugador();
     guardarUsuario(jugador);
     InterfazFinDeJuego vistaFin = new VentanaFinDeJuego();
     ControladorFinDeJuego controlador = 
             new ControladorFinDeJuego(vistaFin, modelo);
     vistaFin.setControlador(controlador);
     vistaFin.arrancar();
     vista.cerrar();
    }
    
     private void mostrarSorpresasAtravesadas(List<Obstruccion> obstrucciones) {
        if (obstrucciones == null) return;
        for (int i = 0; i < obstrucciones.size(); i++) {
            Obstruccion obstruccion = obstrucciones.get(i);
            String nombreObstruccion = obstruccion.getClass().getSimpleName();
            if (nombreObstruccion.contains("Sorpresa") || nombreObstruccion.equals("CambioDeVehiculo")) {
                vista.mostrarSorpresaAtravesada(nombreObstruccion);
            }
        }
    }
}
